package com.example.leetcode.juc;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class BoundedBlockingQueue {

    ReentrantLock lock = new ReentrantLock();
    Condition provider = lock.newCondition();
    Condition consumer = lock.newCondition();
    int capacity;
    Queue<Integer> queue = new LinkedList<>();

    public BoundedBlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void enqueue(int element) throws InterruptedException {
        lock.lock();
        while (!(queue.size() < capacity)) {
            provider.await();
        }
        queue.add(element);
        consumer.signalAll();
        lock.unlock();
    }

    public int dequeue() throws InterruptedException {
        lock.lock();
        while (queue.isEmpty()) {
            consumer.await();
        }
        int res = queue.poll();
        provider.signalAll();
        lock.unlock();
        return res;
    }

    public int size() {
        return queue.size();
    }
}

public class leetcode1188 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        BoundedBlockingQueue blockingQueue = new BoundedBlockingQueue(3);

        // 测试数据量
        int n = 10;
        test(executorService, blockingQueue, n);

        // 关闭线程池
        executorService.shutdown();
    }

    private static void test(ExecutorService executorService, BoundedBlockingQueue blockingQueue, int n) throws ExecutionException, InterruptedException {
        int[] nums = new int[n];
        Random random = new Random();
        for (int i = 0; i < nums.length; i++) {
            nums[i] = random.nextInt(n);
        }
        for (int num : nums) {
            if (num < n / 2) {
                executorService.submit(getPut(blockingQueue, num));
            } else {
                executorService.submit(getPoll(blockingQueue));
            }
        }

        Thread.sleep(1000);
        System.out.println(blockingQueue.size());
    }

    private static Callable<Integer> getPoll(BoundedBlockingQueue blockingQueue) {
        return new Callable<Integer>() {
            @Override
            public Integer call() {
                try {
                    return blockingQueue.dequeue();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    private static Runnable getPut(BoundedBlockingQueue blockingQueue, int element) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    blockingQueue.enqueue(element);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }
}
